﻿
#ifndef SWANLINK_FINANCE_CARDSENDER_ADAPTOR_F5G006_LIB_LIBF5G006_INCLUDE_STOPPABLE_THREAD_H
#define SWANLINK_FINANCE_CARDSENDER_ADAPTOR_F5G006_LIB_LIBF5G006_INCLUDE_STOPPABLE_THREAD_H

#include <thread>
#include <future>
#include <memory>
#include <chrono>

class StoppableThread {
 private:
    std::shared_ptr<std::thread> m_thread;
    std::shared_ptr<std::promise<void>> m_stopSignal;
    std::future<void> m_future;

 private:
    StoppableThread(const StoppableThread&) = delete;
    StoppableThread& operator=(const StoppableThread&) = delete;

 public:
    StoppableThread() = default;
    ~StoppableThread() { join(); }

    template <class _Fn, class... _ArgTypes>
    inline bool start(_Fn&& fn, _ArgTypes&&... args) {
        try {
            m_stopSignal = std::make_shared<std::promise<void>>();
            m_future = m_stopSignal->get_future();
            m_thread = std::make_shared<std::thread>(fn, args...);
            return true;
        }
        catch (...) {
            m_thread.reset();
            m_stopSignal.reset();
            return false;
        }
    }

    inline void interrupt() {
        if (m_stopSignal)
            m_stopSignal->set_value();
    }

    inline bool isInterrupted() {
        auto status = m_future.wait_for(std::chrono::milliseconds(0));
        if (status != std::future_status::timeout)
            return true;
        return false;
    }

    inline void join() {
        if (m_thread) {
            if (m_thread->joinable()) {
                m_thread->join();
            }

            m_thread.reset();
            m_stopSignal.reset();
        }
    }
};

#endif  // SWANLINK_FINANCE_CARDSENDER_ADAPTOR_F5G006_LIB_LIBF5G006_INCLUDE_STOPPABLE_THREAD_H
